/**
 * 游戏场景管理
 * Author      : donggang
 * Create Time : 2016.8.6
 *
 * 需求说明　　　　　　　　　　　
 * 1、智能内存块加载、释放资源管理
 * 2、场景转换时会将新场景的预制路径做为当前内存块名，此场景后面所有加载的资源都属于此内存块
 */

/** 场景层类型 */

game.MemoryConfig              = {};
game.MemoryConfig.replaceScene = false;        // 打印替换场景时的内存情况
game.MemoryConfig.replaceUi    = false;        // 打印替换界面时的内存情况
game.MemoryConfig.removeNode   = false;        // 打印移节点时的内存情况

require("SceneEnum");

var SceneAnimation = require("SceneAnimation");

var SceneManager = cc.Class({
    init : function(){
        this._reference      = new (require("NodeResReference"))();          // 节点资源引用计数管理
        this._isReplaceScene = false;                                        // 是否在替换场景
        this._scenes         = {};                                           // 场景缓存
        this._sceneType      = "SceneBase";                                  // 当前场景类型
    },

    /* 获取资源引用计数管理器（调用这个时除非你知道自己在做什么） */
    __getReference : function(){
        return this._reference;
    },

    /**
     * 设置转场时的场景类型
     * @params  sceneType  场景类型（类型为 Scene、SceneRPG）
     */
    setSceneType : function(sceneType){
        this._sceneType = sceneType;
    },

    /** 获取当前场景 */
    getScene : function(){
        return this._current;
    },

    /**
     * 获取逻辑层节点对象（可能会删除这个方法）
     * return cc.Node
     */
    getLayer : function(name){
        return this._current.getChildByName(name);
    },

    //--------------------------------------------------------------------------------------------------------------

    /**
     * 替换场景（会释放上一个场景标记的的内存）
     * @param newSceneResPath(string)                             新场景资源地址
     * @param operate{
     *      request        : {protocol,params,callback}           预加处理 pomelo 服务器数据请求
     *      preloaded      : "common/image"|['a.png', 'b.png']    预加载文件夹
     *      handler        : function                             预加回调方法
     *      params         : [a,b,c]                              自定义参数，在新界面的added事件中接受
     *      addedToScene   : function                             添加到场景事件
     *      removedToScene : function                             移除场景事件
     * }
     * @param cache   : false                                是否缓存场景
     */
    replaceScene : function(newSceneResPath, operate, cache = false){
        if (this._isReplaceScene) {
            cc.error("正有一个场景在替换中，在当前场景替换完成前不能在替换新场景")
            return;
        }

        // 打开场景事件遮挡
        SceneAnimation.blockEventOpen();

        /** 替换场景完成事件 */
        var onReplaceSceneComplete = function() {
            // 关闭动画
            var isReplace = true;

            // 转场动画 - 预制动画
            isReplace = SceneAnimation.createAnimationByPrefab(this._previous, this._current, operate, function(){
                this._releaseRreviousScene();
            }.bind(this));

            if (isReplace == false){
                this._releaseRreviousScene();
            }
        }.bind(this);

        // 开始替换场景
        this._isReplaceScene = true;
        this._previous       = this._current;

        // 显示当前场景
        this._current = this._scenes[newSceneResPath];
        if (this._current == null){
            var Scene            = require(this._sceneType);
            this._current        = new Scene(newSceneResPath);
            this._current.cache  = cache;
            this._current.load(newSceneResPath, operate, onReplaceSceneComplete);
            this._scenes[newSceneResPath] = this._current;
        }
        this._current.parent = game.canvas;
    },

    /** 释放上一个场景资源 */
    _releaseRreviousScene : function(){
        if (this._previous){
            this._previous.parent = null;

            if (this._previous.cache == false){                // 释放不缓存的场景
                delete this._scenes[this._previous.name];      // 删除场景缓存
                this._previous.dispose();                      // 释放场景对象和相关引用资源

                if (game.MemoryConfig.replaceScene)
                    cc.textureCache.memory();
            }
        }

        this._isReplaceScene = false;

        // 关闭场景事件遮挡
        SceneAnimation.blockEventClose();
    },

    /**
     * 替换界面
     * @param newUiResPath(string)                                新界面资源地址
     * @param operate{
     *      preloaded      : "common/image"|['a.png', 'b.png']    预加载文件夹
     *      handler        : function                             预加回调方法
     *      params         : [a,b,c]                              自定义参数，在新界面的added事件中接受
     *      animationOpen  : path(string),clip(string)            新场景打开动画（两个动画必须同时存在）
     *      animationClose : path(string),clip(string)            旧场景关闭动画（两个动画必须同时存在）
     *      addedToScene   : function                             添加到场景事件
     *      removedToScene : function                             移除场景事件
     *      cleanup        : true                                 是否清理上个界面资源内存
     * }
     */
    replaceUi : function(newUiResPath, operate){
        this._current.replaceUi(newUiResPath, operate);
    },

    /**
     * 添加弹出窗口
     * @param resPath(string)           资源路径
     * @param operate{
     *      preloaded      : "common/image"|['a.png', 'b.png']    预加载文件夹
     *      params         : [a,b,c]                              自定义参数，在新界面的added事件中接受
     *      addedToScene   : function(node)                       添加到场景事件
     *      removedToScene : function()                           移除场景事件
     * }
     */
    addPop : function(resPath, operate){
        this.getLayer(game.SceneLayerType.POP).add(resPath, operate);
    },

    /**
     * 移除弹出窗口
     * @param resPathOrCCNode(string|cc.Node)       资源路径
     * @param cleanup(boolen)                       是清理实例内存
     */
    removePop : function(resPathOrCCNode, cleanup = false) {
        this.getLayer(game.SceneLayerType.POP).remove(resPathOrCCNode, cleanup);
    },

    /**
     * 节点是否存在
     * @param path(string)                                        资源路径
     */
    isExistPopStatic : function(resPath){
        var layer = this.getLayer(game.SceneLayerType.POP_STATIC);
        return layer.isExist(resPath);
    },

    /**
     * 添加静态弹出窗口
     * @param resPath(string)       资源路径
     * @param operate{
     *      preloaded      : "common/image"|['a.png', 'b.png']    预加载文件夹
     *      params         : [a,b,c]                              自定义参数，在新界面的added事件中接受
     *      addedToScene   : function(node)                       添加到场景事件
     *      removedToScene : function()                           移除场景事件
     * }
     */
    addPopStatic : function(resPath, operate) {
        var layer = this.getLayer(game.SceneLayerType.POP_STATIC);
        layer.add(resPath, operate);
    },

    /**
     * 移除静态弹出窗口
     * @param resPathOrCCNode(string|cc.Node)                     资源路径
     * @param cleanup(boolen)                                     是清理实例内存
     */
    removePopStatic : function(resPathOrCCNode, cleanup = false) {
        var layer = this.getLayer(game.SceneLayerType.POP_STATIC);
        layer.remove(resPathOrCCNode, cleanup);
    },

    /**
     * 添加一个 toast 提示
     * @param content(string)                                     提示文本信息
     */
    prompt : function(content) {
        this.getLayer(game.SceneLayerType.TOAST).prompt(content);
    },

    /**
     * 清除所有逻辑层节点
     * @param cleanup(boolen)                       是否清理在实例管理器中的缓存
     */
    clear : function(cleanup = false) {
        this._current.clear(cleanup);
    },

    /**
     * 游戏截图
     * @param fileName(string) 保存的文件名
     * @param cb(Function) 截图完成的回调
     */
    screenShot : function (fileName, cb) {
        if (CC_JSB) {
            var winSize = cc.director.getWinSize();
            var target = new cc.RenderTexture(winSize.width, winSize.height, cc.Texture2D.PIXEL_FORMAT_RGBA8888, gl.DEPTH24_STENCIL8_OES);

            target.begin();
            cc.director.getRunningScene().visit();
            target.end();
            target.saveToFile(fileName, cc.ImageFormat.PNG, true,
            cb.bind(this, jsb.fileUtils.getWritablePath() + fileName));
        }
    },
});

game.scene = module.exports = new SceneManager();
